<?php
require '../utiles.php';

#doc
#	classname:	Neurona
#	scope:		PUBLIC
#
#/doc

class Neurona {
	private $W;
	private $n_entradas;
	
	
	function __construct ($n_entradas, $min = -0.5, $max = 0.5) {
		if (!isInteger($n_entradas))
			die('El numero de entradas ingresado no es un entero');
		
		for ($i = 0 ; $i < $n_entradas + 1; $i++) //Inicializo una entrada de más por el BIAS
			$W[] = mt_rand($min,$max);
	}
	
	public function get_W() {
		return get_n_izq($this->W, $this->n_entradas);
	}
	
	public function set_W($arreglo) {
		if (count($arreglo) != count($this->W))
			die('Error en la dimension del vector W al momento de setear nuevos pesos');
		$this->W = $arreglo;
	}
};


class SOM2D {
	private $n; //Cantidad de neuronas en x
	private $m; //Cantidad de neuronas en y
	
	private $tolerancia;
	private $Neuronas; //Arran de nxm de objetos neuronas
	private $dimension;
	private $iteraciones; //Cuantas patrones fueron entrenados, de esta manera calculamos el radio de activacion
	private $radio_activacion;
	
	//Datos para el entrenamientos
	private $tasa_global = 0.9;
	private $tasa_convergencia = 0.05;
	
	private $tasa_aprendizaje;
	
	function __construct($n_entradas, $neuronas_x, $neuronas_y, $radio_activacion = 4, $varianza_activacion = 20, $max = -0.5, $min = 0.5, $tolerancia = 0.1) {
		$this->Neuronas = array();
		$this->n = $neuronas_x;
		$this->m = $neuronas_y;
		$this->tolerancia = $tolerancia;
		$this->dimension = $n_entradas;
		$this->iteraciones = 0;
		$this->radio_activacion = $radio_activacion;
		
		for ($i = 0; $i < $neuronas_x; $i++) {
			for ($j = 0 ; $j < $neuronas_y; $j++) {
				$this->Neurona[$i][$j] = Neurona($n_entradas, $min, $max);
			}
		}
	}
	
	public function entrenar_con_patron($patron) {
		$patron = array_merge(array(-1), $patron); // se agrega el valor x0 del umbral o bias
		
		//Calculamos la neurona ganadora
		$ganadora = null;
		$distancia_minima = null;
		
		for ($i = 0; $i < $neuronas_x; $i++) {
			for ($j = 0 ; $j < $neuronas_y; $j++) {
				$d = distancia_euclidea( $patron, $this->Neuronas[$i][$j]->get_W() );
				if (!isset($ganadora) || $d < $distancia_minima) {
					$ganadora = array($i,$j);
					$distancia_minima = $d;
				}
			}
		}
		
		//Obtenemos el vecindario de activacion deacuerdo a la iteracion en la que estamos.
		$vecindario = get_conjunto_activacion($ganadora[0], $ganadora[1]);
		
		//Recorremos el vecindario de activacion y actualizamos los pesos de las neuronas que estén ahi
		for ($i = $vecindario[0][0]; $i < $vecindario[0][1]; $i++) {
			for ($j = $vecindario[1][0] ; $j < $vecindario[1][1]; $j++) {
				$Wt = $this->Neuronas[$i][$j]->get_W(); 
				$this->Neuronas[$i][$j]->set_W( suma_vectores( $Wt, dividir_vector(suma_vectores($patron, dividir_vector($Wt,-1)), 1/$this->tasa_aprendizaje)) );
			}
		}
		
		$this->iteraciones++;
	}
	
	public function get_conjunto_activacion($i, $j) {
		if (!isInteger($i) || !isInteger($j))
			die('i o j no son enteros');
		
		if ($this->iteraciones < 1000) {
			//Etapa de Ordenamiento GLOBAL: Medio mapa : 0.7 < tasa < 0.9  ------
			$radio = ($neuronas_x + $neuronas_y)/4;
			$this->tasa_aprendizaje = $this->tasa_global;
			
		}else if($this->iteraciones < 2000) {
			//Etapa de Transicion : Reducir Ag linealmente a 1 : tasa a 0.1 lineal
			//radio = (1-t/1000)*it + 2*t - 1 / t = (m+n)/4
			//tasa = (0.1-tasa_global)/1000 + 2*tasa_global - 0.1
			$t = ($neuronas_x + $neuronas_y)/4;
			$radio = floor( ( (1-$t) / 1000)*$this->iteraciones + 2*$t - 1);
			$this->tasa_aprendizaje = ((0.1-$this->tasa_global)/1000)*$this->iteraciones + 2*$this->tasa_global -0.1;
		}else {
			//Etapa de Ajuste Fino o Convergencia : ag = 0 : tasa cte entre 0.1 y 0.01
			$radio = 0;
			$this->tasa_aprendizaje = $this->tasa_convergencia;
		}
		$radio = $this->get_radio_activacion();
		
		$imin = $i - $radio;
		$imax = $i + $radio;
		$jmin = $j - $radio;
		$jmax = $j + $radio;
		
		if ($imin < 0)
			$imin = 0;
		if ($imax > $neuronas_x)
			$imax = $neuronas_x;
			
		if ($jmin < 0)
			$jmin = 0;
		if ($jmax > $neuronas_y)
			$jmax = $neuronas_y;
			
		return array(
					array($imin, $imax),
					array($jmin, $jmax)
				);
	}
	
};



